<template>
	<div class="three-demo">
		<div class="three-box" ref="threeDemoRef"></div>
		<div ref="dialogRef" id="dialog" style="display: none"></div>

		<!-- <div :class="state.deviceInfo.status == 0 ? 'tip-green' : 'tip-red'">
				<div class="title">设备名称 : {{ state.deviceInfo.selectBoxName }}</div>
				<div class="label-text">
					温度 :
					<span class="mr5" :class="state.deviceInfo.tempState == '0' ? 'label-value-green' : 'label-value-red'"
						>{{ state.deviceInfo.temperature }}
					</span>
					<span :class="state.deviceInfo.tempState == '0' ? 'label-value-green' : 'label-value-red'">
						{{ state.deviceInfo.tempState == '0' ? '正常' : '报警' }}
					</span>
				</div>
				<div class="label-text">
					漏水 :
					<span class="mr5" :class="state.deviceInfo.leakageState == '0' ? 'label-value-green' : 'label-value-red'">{{
						state.deviceInfo.leakage
					}}</span>
					<span :class="state.deviceInfo.leakageState == '0' ? 'label-value-green' : 'label-value-red'">
						{{ state.deviceInfo.leakageState == '0' ? '正常' : '报警' }}
					</span>
				</div>
			</div> -->
		<LoadTransition :loading="state.loading" :percentage="state.progress"></LoadTransition>
	</div>
</template>
<script setup lang="ts" name="three-demo">
import { ref, onMounted, reactive } from 'vue';
// 引入three.js
import * as THREE from 'three';
// 导入附加组件-轨道控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

// 引入gltf模型加载库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
// 引入模型加载过渡动画
import LoadTransition from './loading.vue';
// 引入CSS3渲染器CSS3DRenderer
import { CSS3DRenderer } from 'three/addons/renderers/CSS3DRenderer.js';
// 引入CSS3模型对象CSS3DObject
import { CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js';

// 创建三要素
let scene: any, camera: any, renderer: any;
const threeDemoRef = ref();
const dialogRef = ref();
let controls: any, width: any, height: any;
let model: any;
let css3DRenderer: any; // 3D渲染器

const state: any = reactive({
	randomObject: null, // 随机正常设备对象
	loading: true, // 是否开启加载动画
	progress: 0, // 模型加载进度
});

// 监测div的尺寸变化
const watchDom = () => {
	let ro = new ResizeObserver(() => {
		getDomInfo();
		// 更新画布宽高
		renderer.setSize(width, height);
		// 更新相机的宽高比
		camera.aspect = width / height;
		// 更新相机的近截面和远截面
		camera.updateProjectionMatrix();
		// HTML标签css3Renderer.domElement尺寸重新设置
		css3DRenderer.setSize(width, height);
	});
	ro.observe(threeDemoRef.value);
};

const getDomInfo = () => {
	width = threeDemoRef.value?.offsetWidth;
	height = threeDemoRef.value?.offsetHeight;
};

function init() {
	scene = new THREE.Scene();
	camera = new THREE.PerspectiveCamera(15, width / height, 0.1, 1000);

	// 移动相机位置
	camera.position.set(0, 0, 200);
	camera.lookAt(new THREE.Vector3(0, 0, 0));

	// 添加抗锯齿效果
	renderer = new THREE.WebGLRenderer({ antialias: true });
	// 给画布设置宽高
	renderer.setSize(width, height);
	threeDemoRef.value.appendChild(renderer.domElement);
}

function createControls() {
	controls = new OrbitControls(camera, renderer.domElement);
	// 开启阻尼惯性
	controls.enableDamping = true;
	// 阻尼速度
	controls.dampingFactor = 0.1;
	// 开启旋转
	// controls.autoRotate = true;
	// 旋转的速度
	// controls.autoRotateSpeed = 1;

	// 视角最小距离
	// controls.minDistance = 20;
	// 视角最远距离
	// controls.maxDistance = 5000;
	// 最大角度
	controls.maxPolarAngle = Math.PI / 3;
}

function renderLoop() {
	renderer.shadowMap.enabled = true; // 开启阴影
	// 修改背景颜色
	renderer.setClearColor('#001c55', 1.0);
	// 实时更新性能
	// stats.update();
	controls.update();
	renderer.render(scene, camera);
	css3DRenderer.render(scene, camera);
	requestAnimationFrame(renderLoop);
}

// 监听浏览器窗口的缩放
function renderResize() {
	window.addEventListener('resize', () => {
		getDomInfo();
		// 更新画布宽高
		renderer.setSize(width, height);
		// 更新相机的宽高比
		camera.aspect = width / height;
		// 更新相机的近截面和远截面
		camera.updateProjectionMatrix();

		// HTML标签css3Renderer.domElement尺寸重新设置
		css3DRenderer.setSize(width, height);
	});
}

// 初始化灯光
function initLight() {
	const directionalLight = new THREE.DirectionalLight(0xffffff, 1); //模拟远处类似太阳的光源
	directionalLight.position.set(3, 500, 10).normalize();
	scene.add(directionalLight);

	const ambient = new THREE.AmbientLight(0xffffff, 1); //AmbientLight,影响整个场景的光源
	ambient.position.set(0, 500, 10);
	scene.add(ambient);
}

const selectedEquipment = () => {
	window.addEventListener('click', (event: any) => {
		event.preventDefault();
		// 鼠标控制对象
		const mouse = new THREE.Vector2();
		// 初始化射线辅助器
		const raycaster = new THREE.Raycaster();
		//这里的threeDemoRef.value就是画布所在的div，也就是说，这个是要拿整个scene所在的容器来界定的
		let getBoundingClientRect = threeDemoRef.value?.getBoundingClientRect();
		mouse.x = ((event.clientX - getBoundingClientRect?.left) / threeDemoRef.value?.offsetWidth) * 2 - 1;
		mouse.y = -((event.clientY - getBoundingClientRect?.top) / threeDemoRef.value?.offsetHeight) * 2 + 1;
		//通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
		raycaster.setFromCamera(mouse, camera);

		// 过滤出所有物体
		// const allModels = scene.children.filter((item: any) => item.type == 'Group');
		// const allDevices = allModels[0].children.filter((item: any) => item.name.includes('AU'));
		// 获取与射线相交的对象数组，其中的元素按照距离排序，越近的越靠前
		const intersects: any = raycaster.intersectObjects(scene.children, true);

		// console.log(allModels, 'allModels');
		// console.log(allDevices, 'allDevices');
		// console.log(scene.children, '筛选出所有的children');
		console.log(intersects, '选中的设备');

		// if (intersects.length > 0) {
		// 	// 过滤后的柱子
		// 	const filteredCube = intersects.filter((item: any) => item.object.material.name == '柱子');
		// 	if (filteredCube.length > 0) {
		// 		scene.traverse((child: any) => {
		// 			if (child instanceof THREE.Mesh && child.name === filteredCube[0].object.name) {
		// 				// 保存被选中的设备
		// 				state.initialEquipment = filteredCube[0].object;
		// 				// 修改选中设备的颜色
		// 				const newMaterial = new THREE.MeshBasicMaterial({
		// 					color: 'green',
		// 					transparent: true,
		// 					opacity: 0.8,
		// 					wireframe: false,
		// 					// depthWrite: false,
		// 					side: THREE.DoubleSide,
		// 				});
		// 				child.material = newMaterial;
		// 			}
		// 		});
		// 	}
		// }
	});
};

// 导入模型
const gltfModel1 = () => {
	// glb
	loader.load(
		'model/room1.glb',
		(gltf: any) => {
			model = gltf.scene;
			// 修复设备颜色偏暗的问题
			model.traverse(function (child: any) {
				if (child.isMesh && child.parent.name.includes('AU')) {
					child.frustumCulled = false;
					//模型阴影
					child.castShadow = true;
					//模型自发光
					child.material.emissive = child.material.color;
					child.material.emissive.setHSL(1, 0, 0.35);
					child.material.emissiveMap = child.material.map;
				}
			});

			// 创建标签
			batchCreateCSS3DObject(model);
			scene.add(model);
		},
		(xhr: any) => {
			state.progress = Math.floor((xhr.loaded / xhr.total) * 100); // 计算加载进度百分比
			if (state.progress == '100') state.loading = false;
		},
		// 模型加载错误
		(error: any) => {
			console.log(error, 'error');
		}
	);
};

const createCSS3DRenderer = () => {
	// 创建一个CSS3渲染器CSS3DRenderer
	css3DRenderer = new CSS3DRenderer();
	// css3DRenderer.setSize(width, height);
	css3DRenderer.setSize(180, 220);
	// HTML标签<div id="dialog"></div>外面父元素叠加到canvas画布上且重合
	css3DRenderer.domElement.style.position = 'absolute';
	css3DRenderer.domElement.style.top = '0';
	// css3DRenderer.domElement.style.left = '0px';
	//设置.pointerEvents=none，解决HTML元素标签对threejs canvas画布鼠标事件的遮挡
	css3DRenderer.domElement.style.pointerEvents = 'none';
	threeDemoRef.value.appendChild(css3DRenderer.domElement);
};

const batchCreateCSS3DObject = (model: any) => {
	const equipmentList: any = [];
	const alarmEquipment = [
		{
			name: 'AU02',
			temperature: '35℃', // 温度
			tempState: '1', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '1', // 漏水报警状态 0正常 1报警
			status: 1,
		},
		{
			name: 'AU08',
			temperature: '38℃', // 温度
			tempState: '1', // 温度报警状态 0正常 1报警
			leakage: '60', // 漏水
			leakageState: '1', // 漏水报警状态 0正常 1报警
			status: 1,
		},
		{
			name: 'AU18',
			temperature: '40℃', // 温度
			tempState: '1', // 温度报警状态 0正常 1报警
			leakage: '72', // 漏水
			leakageState: '1', // 漏水报警状态 0正常 1报警
			status: 1,
		},
	];
	model.traverse((obj: any) => {
		if (obj.name.includes('AU')) {
			equipmentList.push(obj);
			// 报警数据持续展示
			alarmEquipment.forEach((item: any) => {
				if (item.name == obj.name) {
					createRandomDialog(obj, item);
				}
			});
		}
	});

	// 过滤出设备正常的数组
	const normalInfo = [
		{
			name: 'AU01',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU03',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU04',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU05',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU06',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU07',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU09',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU10',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU11',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU12',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU13',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU14',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU15',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU16',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU17',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU19',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU20',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU21',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
		{
			name: 'AU22',
			temperature: '35℃', // 温度
			tempState: '0', // 温度报警状态 0正常 1报警
			leakage: '58', // 漏水
			leakageState: '0', // 漏水报警状态 0正常 1报警
			status: 0,
		},
	];
	const filteredEquipment = equipmentList.filter((item: any) => !['AU02', 'AU08', 'AU18'].includes(item.name));
	// 使用 setInterval 每秒随机获取数组中的一个对象
	let index = normalInfo.length - 1;

	setInterval(() => {
		// 移出上一个dialog和连接线
		if (state.randomObject) {
			const currentRandomObject = model.getObjectByName(state.randomObject.name + 'dialog');
			currentRandomObject ? currentRandomObject.parent.remove(currentRandomObject) : '';
			const currentRandomLine = model.getObjectByName(state.randomObject.name + 'line');
			currentRandomLine ? currentRandomLine.parent.remove(currentRandomLine) : '';
			state.randomObject = null;
		}
		index = index == normalInfo.length - 1 ? 0 : ++index;
		const randomInfo = normalInfo[index];
		const randomObject = filteredEquipment.filter((item: any) => item.name == randomInfo.name);
		state.randomObject = randomObject[0];
		createRandomDialog(state.randomObject, randomInfo);
	}, 1000); // 每秒执行一次
};

// 随机dialog
const createRandomDialog = (obj: any, item: any) => {
	// 多个标签-需要克隆复制一份
	const div: any = dialogRef.value.cloneNode();
	div.innerHTML = `
  <div class=${item.status == 0 ? 'tip-green' : 'tip-red'} >
			<div class="title">设备名称 : ${item.name}</div>
			<div class="label-text">
				温度 :
				<span class="mr5" class=${item.tempState == '0' ? 'label-value-green' : 'label-value-red'}
					>${item.temperature}
				</span>
				<span class=${item.tempState == '0' ? 'label-value-green' : 'label-value-red'}>
				 ${item.tempState == '0' ? '正常' : '报警'}
				</span>
			</div>
			<div class="label-text">
				漏水 :
				<span class="mr5" class=${item.leakageState == '0' ? 'label-value-green' : 'label-value-red'}>
          ${item.leakage}
          </span>
				<span class=${item.leakageState == '0' ? 'label-value-green' : 'label-value-red'}>
					${item.leakageState == '0' ? '正常' : '报警'}
				</span>
			</div>
		</div>
		  `;
	// HTML元素转化为threejs的CSS3对象
	const dialog = new CSS3DObject(div);
	//避免标签遮挡canvas鼠标事件
	div.style.pointerEvents = 'none';
	dialog.name = obj.name + 'dialog';
	dialog.scale.set(0.1, 0.1, 1);
	dialog.position.set(0, 6, 0);
	// 判断是否需要旋转
	const nameList = ['AU04', 'AU07', 'AU08', 'AU09', 'AU10', 'AU11', 'AU16', 'AU17', 'AU18', 'AU19', 'AU20'];
	if (nameList.includes(obj.name)) {
		dialog.rotation.set(0, -Math.PI / 2, 0);
	}
	obj.add(dialog);

	// 创建连接线
	const lineGeometry = new THREE.BufferGeometry();
	lineGeometry.name = obj.name + 'line';
	const lineMaterial = new THREE.LineBasicMaterial({
		color: item.status == 0 ? 'green' : '#ff4a4a',
	});
	const pointsArray: any = [];
	pointsArray.push(0, 0, 0);
	pointsArray.push(0, 4, 0);
	lineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(pointsArray, 3));
	lineGeometry.computeBoundingSphere();
	const line = new THREE.Line(lineGeometry, lineMaterial);
	line.name = obj.name + 'line';
	obj.add(line);
};

onMounted(() => {
	// 获取div元素的宽高
	getDomInfo();

	// 初始化
	init();

	// 导入模型
	gltfModel1();

	// 创建轨道控制器(控制相机的运动)
	createControls();

	// 灯光
	initLight();

	// 3D渲染器
	createCSS3DRenderer();

	// 选中设备
	selectedEquipment();

	// 适配
	watchDom();
	renderResize();

	// 循环渲染
	renderLoop();
});
</script>
<style lang="scss" scoped>
.three-demo {
	width: 100%;
	height: 100%;
	overflow: hidden;
	position: relative;

	.three-box {
		width: 100%;
		height: 100%;
	}

	:deep(#dialog) {
		.title {
			font-size: 10px;
			font-family: Source Han Sans CN, Source Han Sans CN;
			font-weight: bold;
		}

		.label-text {
			font-size: 10px;
			font-family: Source Han Sans CN, Source Han Sans CN;
			color: #ccddff;

			.label-value-green {
				color: #5cdd2e;
				font-weight: bold;
			}
			.label-value-red {
				color: #ff4a4a;
				font-weight: bold;
			}
		}

		.tip-green {
			// background-image: url('../../../assets/images/home/threeD/green-bg.svg');
			// background-size: 100% 100%;
			width: 100px;
			height: 50px;
			padding: 5px;
			display: flex;
			flex-direction: column;
			justify-content: center;
			border: 1px solid #329550;
			background-color: rgba(22, 29, 38, 0.5);
			box-shadow: inset 0px 0px 15px 0px #329550;
		}

		.tip-red {
			// background-image: url('../../../assets/images/home/threeD/red-bg.svg');
			// background-size: 100% 100%;
			width: 100px;
			height: 50px;
			padding: 5px;
			display: flex;
			flex-direction: column;
			justify-content: center;
			border: 1px solid #882c2c;
			background-color: rgba(22, 29, 38, 0.5);
			box-shadow: inset 0px 0px 15px 0px #882c2c;
		}
	}
}
</style>
